#include "OCR.h"
#include "utils.h"
#include <iostream>
#include "preprocesamiento.h"

using namespace std;


OCR::OCR(void)
{
}


OCR::~OCR(void)
{
}

void OCR::createSample(){
	//Process image to extract contour
	Mat thr,gray,con;
	Mat src;
	Mat sample;
	Mat response_array;  
	string datos = "567t3a656aa1916g5f42v5b14f4c29243ca1c98ig3321ag2a31z321mr321p0f2d56";
	for (int j = 0; j < 13; j++){
		char nombre [20];
		sprintf(nombre,"digit%d.jpg",j);
		src=imread(nombre,1);
		cvtColor(src,gray,CV_BGR2GRAY);
		threshold(gray,thr,200,255,THRESH_BINARY_INV); //Threshold to find contour
		thr.copyTo(con);

		// Create sample and label data
		vector< vector <Point> > contours; // Vector for storing contour
		vector< Vec4i > hierarchy;
		findContours( con, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE ); //Find contour
		int cont = 0;
		for( int i = 0; i< contours.size(); i++) 
		{
			Rect r= boundingRect(contours[i]); //Find bounding rect for each contour
			if (r.height <= src.rows*3/4) continue;
			rectangle(src,Point(r.x,r.y), Point(r.x+r.width,r.y+r.height), Scalar(0,0,255),2,8,0);
			Mat ROI = thr(r); //Crop the image
			Mat tmp1, tmp2;
			resize(ROI,tmp1, Size(10,10), 0,0,INTER_LINEAR ); //resize to 10X10
			tmp1.convertTo(tmp2,CV_32FC1); //convert to float
			sample.push_back(tmp2.reshape(1,1)); // Store  sample data
			imshow("src",src);
			int c= waitKey();//datos[cont++];// // Read corresponding label for contour from keyoard
			c-=0x30;     // Convert ascii to intiger value
			response_array.push_back(c); // Store label to a mat
			rectangle(src,Point(r.x,r.y), Point(r.x+r.width,r.y+r.height), Scalar(0,255,0),2,8,0);    
		}
	}

	// Store the data to file
	Mat response,tmp;
	tmp=response_array.reshape(1,1); //make continuous
	tmp.convertTo(response,CV_32FC1); // Convert  to float

	FileStorage Data("TrainingData.yml",FileStorage::WRITE); // Store the sample data in a file
	Data << "data" << sample;
	Data.release();

	FileStorage Label("LabelData.yml",FileStorage::WRITE); // Store the label data in a file
	Label << "label" << response;
	Label.release();
	cout<<"Training and Label data created successfully....!! "<<endl;

	imshow("src",src);
	waitKey();
}

void OCR::train(string nombreArch){
	Mat thr,gray,con;
	Mat src=imread(nombreArch,1);
	cvtColor(src,gray,CV_BGR2GRAY);
	threshold(gray,thr,200,255,THRESH_BINARY_INV); // Threshold to create input
	thr.copyTo(con);


	// Read stored sample and label for training
	Mat sample;
	Mat response,tmp;
	FileStorage Data("TrainingData.yml",FileStorage::READ); // Read traing data to a Mat
	Data["data"] >> sample;
	Data.release();

	FileStorage Label("LabelData.yml",FileStorage::READ); // Read label data to a Mat
	Label["label"] >> response;
	Label.release();


	CvKNearest knn;
	knn.train(sample,response); // Train with sample and responses
	cout<<"Training compleated.....!!"<<endl;

	vector< vector <Point> > contours; // Vector for storing contour
	vector< Vec4i > hierarchy;

	//Create input sample by contour finding and cropping
	findContours( con, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
	Mat dst(src.rows,src.cols,CV_8UC3,Scalar::all(0));

	for( int i = 0; i< contours.size(); i=hierarchy[i][0] ) // iterate through each contour for first hierarchy level .
	{
		Rect r= boundingRect(contours[i]);
		if (r.height <= src.rows*3/4) continue;
		Mat ROI = thr(r);
		Mat tmp1, tmp2;
		resize(ROI,tmp1, Size(10,10), 0,0,INTER_LINEAR );
		tmp1.convertTo(tmp2,CV_32FC1);
		float p=knn.find_nearest(tmp2.reshape(1,1), 1);
		char name[4];
		sprintf(name,"%c",(int)p+0x30);
		putText( dst,name,Point(r.x,r.y+r.height) ,0,1, Scalar(0, 255, 0), 2, 8 );
	}
	imshow("src",src);
	imshow("dst",dst);
	imwrite("dest.jpg",dst);
	waitKey();
}

void OCR::init(){
	// Read stored sample and label for training
	Mat sample;
	Mat response;
	FileStorage Data("TrainingData.yml",FileStorage::READ); // Read traing data to a Mat
	Data["data"] >> sample;
	Data.release();

	FileStorage Label("LabelData.yml",FileStorage::READ); // Read label data to a Mat
	Label["label"] >> response;
	Label.release();
	knn.train(sample,response); // Train with sample and responses
}


string OCR::detect(Mat src,Mat &dest){
	Mat aux;
	cvtColor(src,aux,CV_GRAY2BGR);
	Mat thr,gray,con;
	cvtColor(aux,gray,CV_BGR2GRAY);
	threshold(gray,thr,200,255,THRESH_BINARY_INV); // Threshold to create input
	thr.copyTo(con);
	//src.copyTo(con);
	vector< vector <Point> > contours; // Vector for storing contour
	vector< Vec4i > hierarchy;

	//Create input sample by contour finding and cropping
	findContours( con, contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
	vector<Rect> caracteresDetectados;
	for( int i = 0; i< contours.size(); i++ ) 
	{
		Rect r= boundingRect(contours[i]);
		if (r.height <= src.rows*2/3 || r.width <= src.cols*1/10) continue;
		caracteresDetectados.push_back(r);
	}
	sort(caracteresDetectados.begin(),caracteresDetectados.end(),comparatorRect);  
	cadenaPlaca = "";
	char straux [2];
	straux[1] = 0;
	for( int i = 0; i< caracteresDetectados.size(); i++ ) {
		Rect r = caracteresDetectados.at(i);
		Mat ROI = thr(r);
		Mat tmp1, tmp2;
		resize(ROI,tmp1, Size(10,10), 0,0,INTER_LINEAR );
		tmp1.convertTo(tmp2,CV_32FC1);
		float p=knn.find_nearest(tmp2.reshape(1,1), 1);
		char caracter = (int)p+0x30;
		caracter = toupper(caracter);
		cadenaPlaca += caracter;
		straux[0] = caracter;
		rectangle(dest,r,Scalar(0,255,0),2);
		putText(dest,straux, Point(r.x,r.y+r.height), FONT_HERSHEY_PLAIN,3,Scalar(0,0,255), 2, 8);
	}
	return cadenaPlaca;
}

string OCR::detect(Mat src,Mat &dest,int* transiciones){
	dest = src.clone();
	cvtColor(dest,dest,CV_GRAY2BGR);

	Mat img;
	cvtColor(src,img,CV_GRAY2BGR);
	cvtColor(img,img,CV_BGR2GRAY);
	threshold(img,img,200,255,THRESH_BINARY_INV); 
	vector<Rect> caracteresDetectados;
	int x1 = -1, x2 = -1;
	for( int i = 10; i< src.cols-10; i++ ) 
	{
		if (transiciones[i] > 0) 
			x1 = i;
		if (transiciones[i] < 0){
			x2 = i;
			if (x1 == -1) continue;
		} else continue;
		Rect r (x1,0,x2-x1,src.rows);
		x1 = -1;
		if (r.height <= src.rows*2/3 || r.width <= src.cols*1/40) continue;
		caracteresDetectados.push_back(r);
	}
	sort(caracteresDetectados.begin(),caracteresDetectados.end(),comparatorRect);  
	cadenaPlaca = "";
	char straux [2];
	straux[1] = 0;
	for( int i = 0; i< caracteresDetectados.size(); i++ ) {
		Rect r = caracteresDetectados.at(i);
		Mat ROI = img(r);
		Mat tmp1, tmp2,tmp3;
		Rect r2;

		vector< vector <Point> > contours; // Vector for storing contour
		vector< Vec4i > hierarchy;
		/*
		int detectado = 0;
		findContours( ROI.clone(), contours, hierarchy,CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
		for (int j = 0; j < contours.size(); j++){
			r2 = boundingRect(contours[j]);
			if (r2.height > r.height/2 && cumpleRatio(r2.width, r2.height, 0.4,0.35,0.15)){
				detectado = 1;
				break;
			}
		}
		if (detectado==0) 
			continue;
			*/
		resize(ROI,tmp1, Size(10,10), 0,0,INTER_LINEAR );
		tmp1.convertTo(tmp2,CV_32FC1);
		float p=knn.find_nearest(tmp2.reshape(1,1), 1);
		char caracter = (int)p+0x30;
		caracter = toupper(caracter);
		cadenaPlaca += caracter;
		straux[0] = caracter;
		rectangle(dest,r,Scalar(0,255,0),2);
		putText(dest,straux, Point(r.x,r.y+r.height), FONT_HERSHEY_PLAIN,3,Scalar(0,0,255), 2, 8);
	}
	return cadenaPlaca;
}

void OCR::hallarImagenesReconocimiento(Mat imagenColor,vector<vector<Point2f> > cuadrilateros,vector<Mat> &numerosPlaca,vector<Mat> &histogramasHoriz,vector<Mat> &histogramasVert,vector<Mat> &placasDivididas,vector<int*>&divisioneshorizontales){
	for (int i = 0; i < cuadrilateros.size(); i++){
		vector<Point2f> poligono = cuadrilateros[i];
		Mat placaPerspectiva = perspectiva(imagenColor,poligono,distanceBetween(poligono[0],poligono[3])*4,distanceBetween(poligono[0],poligono[1])*4,i);
		if (!cumpleRatio(placaPerspectiva.cols,placaPerspectiva.rows,2,0.3,0.6)) continue; // Se ignora los que no cumplan el ratio

		preprocesarPlaca(placaPerspectiva,placaPerspectiva);
		Mat placaPerspectivaColor = placaPerspectiva.clone();
		cvtColor(placaPerspectivaColor,placaPerspectivaColor,CV_GRAY2BGR);
		int * histogramaVertical = calcularSumaCortesHorizontales(placaPerspectiva);
		Mat histV = Mat::zeros(placaPerspectiva.size(),CV_8U);
		Mat histH = Mat::zeros(placaPerspectiva.size(),CV_8U);
		for (int j = 0; j < placaPerspectiva.rows-1; j++){
			line(histV,Point(histogramaVertical[j]/placaPerspectiva.cols,j),Point(histogramaVertical[j+1]/placaPerspectiva.cols,j+1),Scalar(255));
		}
		int * maximos = hallarMaximosMitad(histogramaVertical,placaPerspectiva.rows,placaPerspectiva.cols);
		placaPerspectiva = Mat(placaPerspectiva,Rect(0,maximos[0],placaPerspectiva.cols,maximos[1]-maximos[0]));
		int * histogramaHorizontal = calcularSumaCortesVerticales(placaPerspectiva);
		truncarHistograma(histogramaHorizontal,histH.cols,histH.rows/10);
		int * transiciones = hallarTransiciones(histogramaHorizontal,placaPerspectiva.cols);
		line(placaPerspectivaColor,Point(0,maximos[0]),Point(placaPerspectivaColor.cols,maximos[0]),Scalar(0,255,0),3);
		line(placaPerspectivaColor,Point(0,maximos[1]),Point(placaPerspectivaColor.cols,maximos[1]),Scalar(0,255,0),3);
		line(histV,Point(0,maximos[0]),Point(placaPerspectivaColor.cols,maximos[0]),Scalar(255),1);
		line(histV,Point(0,maximos[1]),Point(placaPerspectivaColor.cols,maximos[1]),Scalar(255),1);
		for (int j = 0; j < placaPerspectiva.cols-1; j++){
			line(histH,Point(j,histH.rows-histogramaHorizontal[j]),Point(j+1,histH.rows-histogramaHorizontal[j+1]),Scalar(255));
			if (transiciones[j]){
				line(histH,Point(j,0),Point(j,histH.rows),Scalar(255),1);
				if (transiciones[j] > 0)
					line(placaPerspectivaColor,Point(j,0),Point(j,histH.rows),Scalar(255,0,0),3);
				else 
					line(placaPerspectivaColor,Point(j,0),Point(j,histH.rows),Scalar(0,0,255),3);
			}
		}
		histogramasHoriz.push_back(histH);
		histogramasVert.push_back(histV);
		placasDivididas.push_back(placaPerspectivaColor);
		int nObjetos1 = horizontalCrosscut(placaPerspectiva,placaPerspectiva.rows/3);
		int nObjetos2 = horizontalCrosscut(placaPerspectiva,placaPerspectiva.rows*2/3);
		if (min(nObjetos1,nObjetos2) < 8 || max(nObjetos1,nObjetos2) > 14) continue;
		divisioneshorizontales.push_back(transiciones);
		numerosPlaca.push_back(placaPerspectiva);
	}
}

void OCR::reconocerPlaca(Mat imagenColor,vector<vector<Point2f> > cuadrilateros){
	for (int i = 0; i < cuadrilateros.size(); i++){
		char histogramaNameV [30];
		sprintf(histogramaNameV,"zones/histVert%d.jpg",i);
		char histogramaNameH [30];
		sprintf(histogramaNameH,"zones/histHoriz%d.jpg",i);
		char placaOriginalName [30];
		sprintf(placaOriginalName,"zones/placaOriginal%d.jpg",i);
		char placaName [30];
		sprintf(placaName,"zones/placa%d.jpg",i);
		char placaColorName [30];
		sprintf(placaColorName,"zones/placaColor%d.jpg",i);

		vector<Point2f> poligono = cuadrilateros[i];
		Mat placaPerspectiva = perspectiva(imagenColor,poligono,distanceBetween(poligono[0],poligono[3])*4,distanceBetween(poligono[0],poligono[1])*4,i);
		if (!cumpleRatio(placaPerspectiva.cols,placaPerspectiva.rows,2,0.3,0.6)) continue; // Se ignora los que no cumplan el ratio

		Mat placaPerspectivaColor = placaPerspectiva.clone();
		preprocesarPlaca(placaPerspectiva,placaPerspectiva);
		/*
		int nObjetos1 = horizontalCrosscut(placaPerspectiva,placaPerspectiva.rows/3);
		int nObjetos2 = horizontalCrosscut(placaPerspectiva,placaPerspectiva.rows*2/3);
		if (min(nObjetos1,nObjetos2) < 6 || min(nObjetos1,nObjetos2) > 12) continue;*/
		Mat placaOriginal = placaPerspectiva.clone();


		//imshow(placaName,placaPerspectiva);
		int * histogramaVertical = calcularSumaCortesHorizontales(placaPerspectiva);
		Mat histV = Mat::zeros(placaPerspectiva.size(),CV_8U);
		Mat histH = Mat::zeros(placaPerspectiva.size(),CV_8U);
		for (int j = 0; j < placaPerspectiva.rows-1; j++){
			line(histV,Point(histogramaVertical[j]/placaPerspectiva.cols,j),Point(histogramaVertical[j+1]/placaPerspectiva.cols,j+1),Scalar(255));
		}
		int * maximos = hallarMaximosMitad(histogramaVertical,placaPerspectiva.rows,placaPerspectiva.cols);
		placaPerspectiva = Mat(placaPerspectiva,Rect(0,maximos[0],placaPerspectiva.cols,maximos[1]-maximos[0]));
		//dilate(placaPerspectiva,placaPerspectiva,Mat(),Point(-1,-1));

		/****************************************************** OCR ***************************************************************/

		Mat placaDetect;
		cvtColor(placaPerspectiva,placaDetect,CV_GRAY2BGR);
		string numeroPlaca = detect(placaPerspectiva,placaDetect);
		string placa = "";
		if (numeroPlaca.size() < 6 || numeroPlaca.size() > 8) continue;
		if (numeroPlaca.size() == 8){
			for (int j = 1; j < numeroPlaca.size()-1;j++){
				placa += numeroPlaca[j];
			}
		} else {
			if (numeroPlaca.size() == 6)
				placa = numeroPlaca;
			else 
			{
				if (numeroPlaca[0] == 'I' || numeroPlaca[0] == '1'){
					for (int j = 1; j < numeroPlaca.size();j++)
						placa += numeroPlaca[j];
				}else{
					for (int j = 0; j < numeroPlaca.size()-1;j++)
						placa += numeroPlaca[j];
				}
			}
		}

		cout << "Placa detectada"<< i <<": " << placa << endl;

		/**************************************************************************************************************************/

		int * histogramaHorizontal = calcularSumaCortesVerticales(placaPerspectiva);
		truncarHistograma(histogramaHorizontal,histH.cols,histH.rows/10);
		int * transiciones = hallarTransiciones(histogramaHorizontal,placaPerspectiva.cols);

		line(placaPerspectivaColor,Point(0,maximos[0]),Point(placaPerspectivaColor.cols,maximos[0]),Scalar(0,255,0),3);
		line(placaPerspectivaColor,Point(0,maximos[1]),Point(placaPerspectivaColor.cols,maximos[1]),Scalar(0,255,0),3);

		line(histV,Point(0,maximos[0]),Point(placaPerspectivaColor.cols,maximos[0]),Scalar(255),1);
		line(histV,Point(0,maximos[1]),Point(placaPerspectivaColor.cols,maximos[1]),Scalar(255),1);

		for (int j = 0; j < placaPerspectiva.cols-1; j++){
			line(histH,Point(j,histH.rows-histogramaHorizontal[j]),Point(j+1,histH.rows-histogramaHorizontal[j+1]),Scalar(255));
			if (transiciones[j]){
				if (transiciones[j] > 0)
					line(placaPerspectivaColor,Point(j,0),Point(j,histH.rows),Scalar(255,0,0),3);
				else 
					line(placaPerspectivaColor,Point(j,0),Point(j,histH.rows),Scalar(0,0,255),3);
			}
		}
		imshow(placaName,placaDetect);
		imwrite(placaName,placaPerspectiva);
		if (false){
			imwrite(histogramaNameV,histV);
			imwrite(histogramaNameH,histH);
			imwrite(placaOriginalName,placaOriginal);
			imwrite(placaColorName,placaPerspectivaColor);
			imshow(histogramaNameV,histV);
			imshow(histogramaNameH,histH);
			imshow(placaOriginalName,placaOriginal);
			imshow(placaColorName,placaPerspectivaColor);
		}
	}
}